#include "DirectorProcesos.h"
#include "TDA_Lista.h"

/* =============================================================================
 * Declaracion Atributos privados
 * =============================================================================
 */
struct DirectorProcesos{
    tLista nodos;
};

typedef struct DirectorProcesos* _tDirectorProcesos;

/*==============================================================================
 * Declaracion de metodos privados
 *==============================================================================
 */
static tLista directorProcesosGetNodos (tDirectorProcesos pDirectorProcesos);

static void directorProcesosBuscarNodosIdoneos(tDirectorProcesos pDirectorProcesos, tLista nodosIdoneos, tTrabajo pTrabajo);

static tBool directorProcesosCorrerTrabajo(tDirectorProcesos pDirectorProcesos, tLista pNodosIdoneos, tTrabajo pTrabajo);

static tNodo directorProcesosGetNodoPorId (tDirectorProcesos pDirectorProcesos, int pNodoId);

/*==============================================================================
 * Definicion de metodos publicos.
 * =============================================================================
 */

void directorProcesosCrear (tDirectorProcesos* pDirectorProcesos){

    _tDirectorProcesos directorProcesos = NULL;
    
    if (pDirectorProcesos)
    {
        directorProcesos = (_tDirectorProcesos) malloc (sizeof (struct DirectorProcesos));
        if (directorProcesos)
            listaCrear(&(directorProcesos->nodos), nodoDestruir);
    }
    
    *pDirectorProcesos = directorProcesos;
}

//------------------------------------------------------------------------------

void directorProcesosDestruir (tDirectorProcesos pDirectorProcesos){
    
    if (pDirectorProcesos)
    {
        listaDestruir(directorProcesosGetNodos(pDirectorProcesos));
        free ((_tDirectorProcesos)pDirectorProcesos);
    }
}

//------------------------------------------------------------------------------

tBool directorProcesosDeclararNodo (tDirectorProcesos pDirectorProcesos, int pNodoId, unsigned long int pNodoMemoriaDisponible){

    tBool val = FALSE;    
    
    if (pDirectorProcesos)
    {
        tNodo nodo;
        nodoCrear(&nodo, pNodoId, pNodoMemoriaDisponible);
        val = listaAgregarFinal(directorProcesosGetNodos(pDirectorProcesos), nodo);
    }
    
    return val;
}

//------------------------------------------------------------------------------

void directorProcesosQueryNodo (tDirectorProcesos pDirectorProcesos, int pNodoId){

    if (pDirectorProcesos)
    {
        tNodo nodo = directorProcesosGetNodoPorId (pDirectorProcesos, pNodoId);

        if (nodo)
        {
            if (ENCENDIDO == nodoGetEstado(nodo))
                if (CORRIENDO == nodoGetEstadoProcesador(nodo))
                {
                    // Imprimo la info del job.
                    tTrabajo trabajo = nodoGetTrabajo(nodo);
                    fprintf (stdout, "JOB(%d): %s\n", trabajoGetPrioridad(trabajo), trabajoGetPathEjecutable(trabajo));
                }   
                else
                    fprintf (stdout, "AVAILABLE\n");
            else
                fprintf (stdout, "OFF\n");
        }
        else
            fprintf (stderr, "QUERY %d: nodo %d inexistente\n", pNodoId, pNodoId);
    }
}

//------------------------------------------------------------------------------

tBool directorProcesosEncenderNodo (tDirectorProcesos pDirectorProcesos, int pNodoId){
    
    tBool val = FALSE;
    
    if (pDirectorProcesos)
    {
        tNodo nodo = directorProcesosGetNodoPorId(pDirectorProcesos, pNodoId);
        
        if (nodo)
            val = ENCENDIDO == nodoEncender(nodo);
        else
            fprintf (stderr, "ON %d: nodo %d inexistente\n", pNodoId, pNodoId);
    }
    return val;
}

//------------------------------------------------------------------------------

tBool directorProcesosApagarNodo (tDirectorProcesos pDirectorProcesos, int pNodoId){

    tBool val = FALSE;
    
    if (pDirectorProcesos)
    {
        tNodo nodo = directorProcesosGetNodoPorId(pDirectorProcesos, pNodoId);

        if (nodo)
            if (CORRIENDO == nodoGetEstadoProcesador(nodo))
                fprintf (stderr, "OFF %d: no se puede apagar nodo %d, se encuentra procesando\n", pNodoId, pNodoId);
            else
                val = APAGADO == nodoApagar(nodo);
        else
            fprintf (stderr, "OFF %d: nodo %d inexistente\n", pNodoId, pNodoId);
    }
    
    return val;
}

//------------------------------------------------------------------------------

tBool directorProcesosEjecutarTrabajo (tDirectorProcesos pDirectorProcesos, tTrabajo pTrabajo){
    
    tBool corriendo = FALSE;
    
    if (pDirectorProcesos && pTrabajo)
    {
        tLista nodosIdoneos;
        listaCrear(&nodosIdoneos, &DestructorDato);

        if (nodosIdoneos)
        {
            directorProcesosBuscarNodosIdoneos(pDirectorProcesos, nodosIdoneos, pTrabajo);

            if (!listaVacia(nodosIdoneos))
              corriendo = directorProcesosCorrerTrabajo(pDirectorProcesos, nodosIdoneos, pTrabajo);
            
            listaDestruir(nodosIdoneos);
        }
    }
    
    return corriendo;
}

//------------------------------------------------------------------------------

void directorProcesosGetTrabajosEnEjecucion (tDirectorProcesos pDirectorProcesos, tLista pTrabajos){
    
    if (pDirectorProcesos && pTrabajos)
    {
        tIteradorLista iterador = listaObtenerIterador(directorProcesosGetNodos(pDirectorProcesos));
        
        while (listaIteradorMoverSiguiente(iterador))
        {
            tNodo nodo = listaIteradorActual(iterador);
            
            if (CORRIENDO == nodoGetEstadoProcesador(nodo))
                listaAgregarFinal(pTrabajos, nodoGetTrabajo(nodo));        
        }
        listaLiberarIterador(iterador);
    }
}

//------------------------------------------------------------------------------

void directorProcesosImprimir(tDirectorProcesos pDirectorProcesos, tOutStream pOutStream){
    
    if (pDirectorProcesos && pOutStream)
    {
        tIteradorLista iterador = listaObtenerIterador(directorProcesosGetNodos(pDirectorProcesos));
        while (listaIteradorMoverSiguiente(iterador))
        {
            tNodo nodo = (tNodo) listaIteradorActual(iterador);
            fprintf (pOutStream, "\nNodo => [Id: %d][Estado: %d][Estado_Procesador: %d][Memoria: %lu]", nodoGetId(nodo), nodoGetEstado(nodo), nodoGetEstadoProcesador(nodo), nodoGetMemoria(nodo));
            fprintf (pOutStream, "\n    ");
            trabajoImprimir (nodoGetTrabajo(nodo), pOutStream);
        }
        listaLiberarIterador(iterador);
    }
}

//==============================================================================

static tLista directorProcesosGetNodos (tDirectorProcesos pDirectorProcesos){

    tLista val = NULL;
    
    if (pDirectorProcesos)
        val = ((_tDirectorProcesos) pDirectorProcesos)->nodos;
    
    return val;
}

//------------------------------------------------------------------------------

static void directorProcesosBuscarNodosIdoneos(tDirectorProcesos pDirectorProcesos, tLista nodosIdoneos, tTrabajo pTrabajo){
    
    if (pDirectorProcesos && nodosIdoneos && pTrabajo)
    {
        tIteradorLista iterador = listaObtenerIterador(directorProcesosGetNodos(pDirectorProcesos));
        while (listaIteradorMoverSiguiente(iterador))
        {
            tNodo nodo = listaIteradorActual(iterador);
            
            if (nodoEsEjecutable(nodo, pTrabajo))
                listaAgregarFinal(nodosIdoneos, nodo);
        }
        listaLiberarIterador(iterador);
    }
}

//------------------------------------------------------------------------------

static tBool directorProcesosCorrerTrabajo(tDirectorProcesos pDirectorProcesos, tLista pNodosIdoneos,tTrabajo pTrabajo){
    
    tBool corriendo = FALSE;
    
    if (pDirectorProcesos && pNodosIdoneos && pTrabajo)
    {
        int nodosNecesarios = trabajoGetPotenciaProceso(pTrabajo);

        if (listaLongitud(pNodosIdoneos) >= nodosNecesarios)
        {
            tIteradorLista iterador = listaObtenerIterador(pNodosIdoneos);
            while  (listaIteradorMoverSiguiente(iterador) && nodosNecesarios > 0)
            {
                tNodo nodo = listaIteradorActual(iterador);
                nodoEjecutar(nodo, pTrabajo);
                nodosNecesarios--;
                
                if (nodosNecesarios > 0)
                    pTrabajo = trabajoClonar(pTrabajo);
            }
            listaLiberarIterador(iterador);
            corriendo = TRUE;
        }
    }   
    return corriendo;
}

//------------------------------------------------------------------------------

static tNodo directorProcesosGetNodoPorId (tDirectorProcesos pDirectorProcesos, int pNodoId){
    
    tNodo nodoBuscado = NULL;
    
    if (pDirectorProcesos)
    {
        tIteradorLista iterador = listaObtenerIterador (((_tDirectorProcesos) pDirectorProcesos)->nodos);
        while (listaIteradorMoverSiguiente(iterador) && NULL == nodoBuscado)
        {
            tNodo nodo = listaIteradorActual(iterador);
            if (nodoGetId(nodo) == pNodoId)
                nodoBuscado = nodo;
        }
        listaLiberarIterador(iterador);
    }
    return nodoBuscado;
}



